/**
 * @author Created by qiush on 2017-06-08.
 */

const {protocol, ipcMain} = require('electron');
const fs = require('fs');
const fileUtil = require('./fileUtil');

/** PNG 图片 */
const REG_PNG = /\.png$/i;
/** JPEG 图片 */
const REG_JPEG = /\.jpe?g$/i;
/** CSS 文件 */
const REG_CSS = /\.css$/i;

/** 资源列表: name -> resource data */
const resources = {};

/**
 * 处理二进制的资源
 * @param name 资源名称, 一般是资源的路径, 如果没有传递content, 则从这个路径读取
 * @param content 资源内容, 使用Base64 编码的二进制数据
 * @returns {Promise.<*>} 资源内容, Buffer
 * @private
 */
async function _handleBinary(name, content) {
  const data = await (
    content !== undefined ?
      Buffer.from(content, 'base64') :
      fileUtil.readBinaryFile(name)
  );
  resources[name] = data;
  return data;
}

/**
 *处理文本的资源
 * @param name 资源名称, 一般是资源的路径, 如果没有传递content, 则从这个路径读取
 * @param content 资源内容
 * @returns {Promise.<*>} 资源内容, string
 * @private
 */
async function _handleString(name, content) {
  const data = await (content === undefined ? fileUtil.readFile(name) : content);
  resources[name] = data;
  return data;
}

const resourceUtil = {
  /**
   * 获取指定的资源
   * @param name 资源名称
   * @returns {*} 资源, Buffer | string
   */
  getResource(name) {
    return resources[name];
  },

  /**
   * 获取指定的资源
   * @param resourceUrl 资源的URL, 一般是 buf://资源名称 这样的形式
   * @returns {*} 资源的内容, 如果是二进制的资源, 则会转换成对应的Base64 的形式, string | null
   */
  getResourceFromUrl(resourceUrl) {
    if (!resourceUrl.match(/^(buf|str):\/\//i)) {
      return null;
    }

    if (resourceUrl.startsWith('buf')) {
      const name = resourceUrl.substring('buf://'.length);
      const resource = this.getResource(name); // Buffer
      return {
        name,
        resource: resource.toString('base64')
      };
    }

    const name = resourceUrl.substring('str://'.length);
    const resource = this.getResource(name); // string
    return {
      name,
      resource
    }
  },

  /**
   * 添加资源
   * @param resources 资源对象, key 是资源名称, value 是资源内容
   * @returns {Promise.<*>} 资源内容列表
   */
  addResources(resources) {
    if (!resources) {
      return Promise.resolve();
    }

    const promises = Object.keys(resources).map(async name => {
      return await this.addResource(name, resources[name]);
    });

    return Promise.all(promises);
  },

  /**
   * 添加资源
   * @param name 资源名称, 一般是资源的路径, 如果没有传递content, 则从这个路径读取
   * @param content 资源内容, 如果是二进制的资源, 则这里是使用了Base64 编码的文本数据
   * @returns {Promise.<*>} 资源内容
   */
  async addResource(name, content) {
    if (name.match(REG_PNG) || name.match(REG_JPEG)) {
      return await _handleBinary(name, content);
    }

    if (name.match(REG_CSS)) {
      return await _handleString(name, content);
    }

    return Promise.reject(`不能正确处理这个资源: ${name}`);
  },

  /**
   * 删除资源
   * @param name 资源名称
   */
  removeResource(name) {
    delete resources[name];
  }
};

/** 从页面发送过来的添加资源的请求, 传递资源的路径过来 */
ipcMain.on('resource', (event, path) => resourceUtil.addResource(path));

/**
 * 获取资源的名称
 * @param url 页面上访问资源所使用的URL
 * @param protocol 所使用的协议
 * @returns {string} 资源的名称
 * @private
 */
function _getUrl(url, protocol) {
  const startIndex = `${protocol}://`.length;
  const paramIndex = url.indexOf('?');
  const endIndex = paramIndex > 0 ? paramIndex : url.length;
  return decodeURI(url.substring(startIndex, endIndex));
}

function _getMimeType(url) {
  if (url.endsWith('.css')) {
    return 'text/css';
  }
  return 'text/plain';
}

/** 添加str 协议, 页面可以使用 str://资源名称 的形式来访问资源 */
protocol.registerStringProtocol('str', (request, callback) => {
  const url = _getUrl(request.url, 'str');
  callback({
    data: resources[url],
    mimeType: _getMimeType(url),
    charset: 'UTF-8'
  });
});

/** 添加buf 协议, 页面可以使用 buf://资源名称 的形式来访问资源 */
protocol.registerBufferProtocol('buf', (request, callback) => {
  const url = _getUrl(request.url, 'buf');
  console.info(url, resources[url]);
  callback(resources[url]);
});

module.exports = resourceUtil;
